package com.huwaiwai.lock.simple;

import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.LockSupport;

/**
 * @author 歪大哥😁
 * @date 2020-04-22.
 */
public class SimpleAQS {

    public final void acquire(int arg) {
         int state = this.state.get();
         Thread currentThread = Thread.currentThread();

         if (this.currentThread == currentThread.getId()) {
             // 重入
             this.state.compareAndSet(state, state + arg);
         } else if (state == 0 && !hasQueue() && this.state.compareAndSet(state, state + arg)) {
             // 可以尝试获取 获取成功  设置当前占用线程
             this.currentThread = currentThread.getId();
         } else {
             // 先将当前线程包装成Node对象后，加入到队列中
             Node node = addQueue(currentThread);
             // 然后开始挂起当前线程
             if (acquireQueued(node, arg)) {
                 Thread.currentThread().interrupt();
             }
         }
    }

    private boolean acquireQueued(Node node, int arg) {
        boolean interrupt = false;
        for (;;) {
            Node prev = node.prev;
            if (this.head == prev && this.state.compareAndSet(0, arg)) {
                // 获取成功
                this.head = node;
                prev.next = null;
                this.currentThread = node.thread.getId();
                return interrupt;
            }
            if (shouldParkAfterFailedAcquire(prev, node) && parkAndCheckInterrupt()) {
                // 会进这里 说明当前线程已经被中断了
                // 需要取消排队  然后设置中断状态
                // 意思是 就算线程被中断了 也是需要拿到锁以后 才返回
                interrupt = true;
            }
        }
    }

    /**
     * 挂起并且检查中断状态
     */
    private boolean parkAndCheckInterrupt() {
        LockSupport.park(this);
        return Thread.interrupted();
    }

    /**
     * 判断当前线程是否可以被挂起
     * 条件:找到一个有效的前置节点  前置节点的status == -1;
     */
    private boolean shouldParkAfterFailedAcquire(Node prev, Node node) {
        // 获取失败 是否可以被挂起了  可以被挂起的条件是 当前节点的前置节点状态改成-1
        int ws = prev.status;
        if (ws == Node.SIGNAL) {
            return true;
        }
        if (ws > 0) {
            // 前置已经取消  向前寻找一个有效的节点
            do {
                node.prev = prev = prev.prev;
            } while (prev.status > 0);
            prev.next = node;
        } else {
            // 到这里 说明ws == 0  尝试把其设置成-1 表示前置节点的后续节点(当前节点)处于等待状态
            prev.status = -1;
            return true;
        }
        return false;
    }

    private Node addQueue(Thread currentThread) {
        Node node = new Node(currentThread);
        // 无限循环 直到入队成功
        for (;;) {
            Node t = this.tail;
            if (t == null) {
                if (this.addFlag.compareAndSet(false, true)) {
                    // 需要初始化  初始化一个不包含线程的头节点 将当前检点作为其 后驱节点 并赋值给队尾
                    node.prev = this.head = new Node(null);
                    this.tail = head.next = node;
                    this.addFlag.compareAndSet(true, false);
                    return node;
                }
            } else {
                if (this.addFlag.compareAndSet(false, true)) {
                    // 拿到操作权以后 将当前节点 添加到队尾 并且成为队尾
                    node.prev = t;
                    t.next = node;
                    this.addFlag.compareAndSet(true, false);
                    return node;
                }
            }
        }
    }

    /**
     * 是否有等待中的线程如果有则直接进入队尾
     * @return
     */
    private boolean hasQueue() {
        return this.head != null && this.head == this.tail;
    }

    public final boolean release(int arg) {
        int state = this.state.get();
        int c = state - arg;
        // 既然要释放 那么必然是当前线程
        if (this.currentThread != Thread.currentThread().getId() || c < 0) {
            throw new IllegalStateException();
        }
        boolean free = false;
        if (c == 0) {
            this.currentThread = 0;
            free = true;
        }
        this.state.compareAndSet(state, c);
        if (free) {
            unparkSuccessor();
        }
        return free;
    }

    /**
     * 唤醒离头节点最近的等待线程 并且移除队列中已经取消的线程
     */
    private void unparkSuccessor() {
        Node head = this.head;
        if (head == null) {
            return;
        }
        if (head.status == Node.SIGNAL) {
            System.out.println("唤醒了" + head.next.thread.getName());
            LockSupport.unpark(head.next.thread);
            return;
        }

        if (this.head == this.tail) {
            return;
        }
        Node s = head.next;
        if (s == null || s.status > 0) {
            s = null;
            for (Node t = tail; t != null && t != head; t = t.prev) {
                if (t.status <= 0) {
                    s = t;
                }
            }
        }
        if (s != null) {
            LockSupport.unpark(s.thread);
        }
    }

    public SimpleAQS() {

    }

    volatile Node head;

    volatile Node tail;

    AtomicInteger state = new AtomicInteger(0);

    AtomicBoolean addFlag = new AtomicBoolean(false);

    volatile long currentThread;

    static class Node {

        Node(Thread t) {
            this(t, 0);
        }

        private Node(Thread t, int status) {
            this.thread = t;
            this.status = status;
        }

        Thread thread;

        Node prev;

        Node next;

        int status;

        static final int SIGNAL = -1;
        static final int CANCEL = 1;

    }

}
